package com.dou.cloud.bridge.redis.handler;

import com.due.basic.tookit.exception.LogicException;
import com.due.basic.tookit.function.DueVoidProducer;
import com.due.basic.tookit.utils.DateUtil;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import static com.due.basic.tookit.constant.RedisConstant.AFTER_TRYING;
import static com.due.basic.tookit.constant.RedisConstant.DO_NOT_OPERATE_FREQUENTLY;
import static com.due.basic.tookit.enums.ErrorEnum.LOCK_EXPIRED_RELEASED;
import static com.due.basic.tookit.enums.ErrorEnum.REDISSON_CLIENT_INITIALIZED;

/**
 * @Author daizl
 * @Date 2024 06 19 18 04
 **/
public abstract class AbstractDistributedLockHandler implements DistributedLockHandler {


    protected RedissonClient redissonClient;


    /**
     * 联锁
     *
     * @param dueVoidProducer 业务逻辑
     * @param locks           锁集合
     * @param redissonClients 客户端集合
     */
    public void multiLock(DueVoidProducer dueVoidProducer, List<RLock> locks, long waitTime, long leaseTime, TimeUnit timeUnit, RedissonClient... redissonClients) {
        for (RedissonClient client : redissonClients) {
            locks.add(client.getLock("lock" + (locks.size() + 1)));
        }
        RedissonMultiLock lock = new RedissonMultiLock(locks.toArray(new RLock[0]));
        try {
            lock.lock();
            // 这里可以添加需要执行的并发操作
            dueVoidProducer.exec();
            lock.tryLock(waitTime, leaseTime, timeUnit);
        } catch (InterruptedException e) {
            throw new LogicException(LOCK_EXPIRED_RELEASED);
        } catch (Exception ex) {
            throw new LogicException(ex);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 红锁
     *
     * @param dueVoidProducer 业务逻辑
     * @param locks           锁集合
     * @param redissonClients 客户端集合
     */
    public void redLock(DueVoidProducer dueVoidProducer, List<RLock> locks, long waitTime, long leaseTime, TimeUnit timeUnit, RedissonClient... redissonClients) {
        for (RedissonClient client : redissonClients) {
            locks.add(client.getLock("lock" + (locks.size() + 1)));
        }
        RedissonRedLock lock = new RedissonRedLock(locks.toArray(new RLock[0]));
        try {
            lock.lock();
            // 这里可以添加需要执行的并发操作
            dueVoidProducer.exec();
            lock.tryLock(waitTime, leaseTime, timeUnit);
        } catch (InterruptedException e) {
            throw new LogicException(LOCK_EXPIRED_RELEASED);
        } catch (Exception ex) {
            throw new LogicException(ex);
        } finally {
            lock.unlock();
        }

    }

    /**
     * 读写锁
     *
     * @param supplier  业务逻辑
     * @param key              锁的key
     * @param waitTime         等待时间
     * @param leaseTime        锁的租期
     * @param timeUnit         锁的租期单位
     * @param isWriteOperation 是否为写操作
     * @param <T>              业务逻辑返回值类型
     * @return 业务逻辑返回值
     */
    public <T> T executeOperation(Supplier<Object> supplier, String key, long waitTime, long leaseTime, TimeUnit timeUnit, boolean isWriteOperation) {
        isRedissonClient();
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(key);
        RLock lock = isWriteOperation ? readWriteLock.writeLock() : readWriteLock.readLock();
        try {
            boolean isLocked = lock.tryLock(waitTime, leaseTime, timeUnit);
            if (isLocked) {
                supplier.get(); // 执行业务逻辑
            } else {
                throw new LogicException(DO_NOT_OPERATE_FREQUENTLY + leaseTime + DateUtil.toChinese(timeUnit) + AFTER_TRYING);
            }
        } catch (IllegalMonitorStateException ex) {
            lock.unlock();
            throw new LogicException(LOCK_EXPIRED_RELEASED);
        } catch (Exception ex) {
            throw new LogicException(ex);
        }
        return null;
    }

    /**
     * 检查redissonClient是否为空
     */
    public void isRedissonClient() {
        if (redissonClient == null) {
            throw new LogicException(REDISSON_CLIENT_INITIALIZED);
        }
    }

    @Autowired
    public void setAbstractDistributedLockHandler(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

}
